library(openintro)

source("covid_study_data_plotter.R")
## [1] "~~~~~ LOADING DATA ~~~~~~"
## [1] "Founds cached data folder."
## [1] "HRR shapes data loaded from local cache."
## [1] "Loaded zip hrr crosswalk data from local cache."
## [1] "Loaded ZCTA codes and population data from local cache."
## [1] "Approximated zip code population using ZCTA populations: "
## [1] "--After joining US state zip codes with ZCTAs, We keep about 98.8 % of the population, loosing 3,738,434 people."
## [1] "--zctas also account for the population in us territories, which have a total population of 3,623,895 according to wikipedia."
## [1] "--This pushes the retained popoulation of the states closer to 99.96 percent"

Project Description

View the Shiny App here: https://cinderscript.shinyapps.io/Covid-Vaccination-and-Hospital-Strain/

This project is available on GitHub

About

This is a study of vaccination rate and hospital bed usage in the United States inspired by the Washington Post article Mapping America’s hospitalization and vaccination divide. In this project we will be recreating the USA map found in the article and adding interactivity so different dates and variables can be selected.

Map By Zach Levitt and Dan Keating:

Washington Post Bivariate Choropleth Map

Methods

Vaccination data is provided at the county level by the CDC and hospital bed usage data is provided by HealthData.gov. These variables are visualized with a bivariate choropleth map of Hospital Referral Regions in the United States.

Vaccination Rate is defined by county and HRRs are defined by zip code. We can’t use county data to calculate the vaccination rate of an HRR because these regions overlap. Zip codes in one HRR can live in different counties, and Zip codes in different counties can live in the same HRR.

We need to know both the population of each HRR and the vaccination rate of each part of that population.

We determine the vaccination rate of the HHRs by averaging the vaccination rate (given by county) of the zip codes in that HRR. The individual zip code’s vaccination rate needs to be weighted by that zip code’s population. Population data is obtained from the United States Census Bureau, which is unfortunately not counted by zip code, but by blocks that make up the congressional districts. To estimate the population of zip codes, we will use the 2010 census zip code tabulation records, which approximate the zip codes in which the congressional district blocks lay.

To find all zip codes in an HRR we will use a Zip Code to HRR crosswalk.

Data Sources

Summery

  1. Hospital Bed Usage in USA - per hospital
  2. Vaccination Rates in USA - per county
  3. Population Census in USA - per zcta
  4. HRR Geography in USA - per HRR number
  5. County Geography in USA - per county
  6. Crosswalk for Zip Code and HRR number

Anytime a new file is downloaded, that file is cached in the “cached-data” folder. Anytime a request is made for a dataset by date, this folder is checked first.

Local Data Caching

All data sources are downloaded from the internet. Only the needed portions of the datasets are requested from the corresponding endpoints. Before downloading, this application first checks if the dataset has already been downloaded in a local cache file. Whenever a new portion of a dataset is downloaded, it is saved to the cache folder local to the application folder.

Vaccination Rates Data per US County

Vaccination rates are obtained from the CDC: https://data.cdc.gov/Vaccinations/COVID-19-Vaccinations-in-the-United-States-County/8xkx-amqh. This dataset is large, so instead of downloading the whole dataset, this application accesses it through the SODA API and retrieves only the relevant rows and columns.

The “COVID-19 Vaccinations in the United States,County” data provides counts and percentages of people who have been vaccinated in each county of the United States.

The variables retrieved are:

  • fips
  • series_complete_pop_pct: “Percent of people who have completed a primary series (have second dose of a two-dose vaccine or one dose of a single-dose vaccine) based on the jurisdiction and county where vaccine recipient lives.”
  • administered_dose1_pop_pct: “Percent of Total Pop with at least one Dose by State of Residence”
  • booster_doses_vax_pct: “Percent of people who completed a primary series and have received a booster (or additional) dose.”

Hospital Capacity Data of USA per Hospital

Hospital bed usage counts is obtained from HealthData.gov: https://healthdata.gov/Hospital/COVID-19-Reported-Patient-Impact-and-Hospital-Capa/anag-cw7u. This dataset is large, so instead of downloading the whole dataset, this application accesses it through the SODA API and retrieves only the relevant rows and columns.

The “COVID-19 Reported Patient Impact and Hospital Capacity by Facility” data provides counts on hospital bed utilization that is aggregated weekly.

The variables retrieved are:

  • Hospital_name and fips_code
  • inpatient_beds_7_day_avg: “Average number of total number of staffed inpatient beds in your hospital including all overflow, observation, and active surge/expansion beds used for inpatients (including all ICU beds) reported in the 7-day period.”
  • inpatient_beds_used_7_day_avg: “Average of total number of staffed inpatient beds that are occupied reported during the 7-day period.”
  • inpatient_beds_used_covid_7_day_avg: “Average of reported patients currently hospitalized in an inpatient bed who have suspected or confirmed COVID-19 reported during the 7-day period.”

Inpatient bed counts are used instead of total bed counts because many hospitals that only have inpatient bed data do not include data for inpatient and outpatient totals.

Geographic Shape Data

US County Shape Data

County shape data is obtained from the R package Albersusa.

Hospital Referral Region Shape Data

Shape data for USA HRR regions are downloaded from arcgis.com using their “FeatureServer” REST api.

https://www.arcgis.com/home/item.html?id=46bf6790c4e0455e9379ee9769b1a5ab

Crosswalk Data

HRR number to zip code translation (2019)

This crosswalk is obtained from Dartmouth Atlas as a zip file.

https://data.dartmouthatlas.org/supplemental/#crosswalks

Calculated Data

Ratios for Hospital Bed Usage per Hospital

We also calculate:

  • bed_usage_ratio: The ratio of hospital beds used out of 100 beds (inpatient)
  • covid_bed_usage_ratio: The ratio of hospital beds used by covid patients out of 100 beds (inpatient)
  • covid_bed_usage_total_bed_usage_ratio: The ratio of hospital beds used by covid patients out of all used beds (inpatient)

Average Bed Ratios per Region

The hospital bed dataset contains records for each hospital. For mapping, each map region will need to represent the average of all hospitals present in that region.

Hospital Referral Region population

A dataset is needed that tracks the population percentages of each ZCTA in each county for the function that calculates vaccination rates of HRRs. This dataset is generated by joining ZCTA populations with zip codes. The partial populations of ZCTAs in a given zip code (due to overlapping zcta and zipcode regions) is accounted for.

Processed by Source: covid_study_data_wrangler.R, which creates functions for generating graph-able datasets

Calculate HHR vaccination rate

We are using population data for zip code tabulation areas because we don’t have counts for zip codes. ZCTAs will be our proxy for zip codes. Because zcta’s don’t line up exactly with zip codes, the same zcta can be part of more than one county. That is why it is important to know how much of the ZCTA’s population is in each county.

The Process:

  1. Join the vaccination data for 2021/09/24 with population zip code data by county.
  • Now we know the vaccination rate of each zip code
  1. Join with the zip hhr crosswalk by zip code to zcta.
  • Now we know the hrr each zip code is in along with that zip code’s vaccination rate
  1. Join with the known population counts of zip codes by zip code.
  • Now we know the slice of population of a zip code inside each county along with that population slice’s vaccination rate
  1. Calculate number of vaccinated people in each zip code’s population slice inside each county for each hrr
  2. Divide the number of vaccinated in zip slice by the total hrr population that zip code is in.
  • This gives us the “slice vaccination percentage” that contributes to the vaccination percentage of the hrr
  1. Add up all of the sliced vaccination percentages for each hrr.

Function defined in Source: covid_study_data_wrangler.R :: calculate_hrr_vaccination_rates(date)

Data Cleaning

Texas

There is a lot of missing data for both hospital bed usage and vaccination rates. TEXAS records before 2021-10-22 are removed. Before 2021-10-22, Texas had problems with their recorded vaccination rates and they are recorded as ‘0’ in the dataframes. These records are removed so they don’t throw off the percentages of graphed stats and automatic range scaling of the graphs.

Information: https://www.texastribune.org/2021/01/20/texas-coronavirus-vaccine-data/

Nonsensical Data

Replace 0% single dose percentages with NA where appropriate Also, Many records for the single dose == 0 while series complete is > 0. This isn’t possible so these values probably were not recorded. Replace them with NA so that future data wrangling ignores those values in calculations.

By 2021-02-01 all HHR region states have some single dose percentage TX is the only state that has 0%, which we don’t have collected data for. After 2021-01-31, all entries that have 0% single dose are removed. (All entries that are either before 2021-02-01 or have some percentage of single dose are kept)

Source Files

Source File | Description

covid_study_data_loader.R | loads all required data covid_study_data_wrangler.R | functions for generating graph-able datasets covid_study_data_plotter.R | functions for generating ggplot and ggplotly graphs app.R | shiny app

Source Dependency:

app.R –> covid_study_data_plotter.R –> covid_study_data_wrangler.R –> covid_study_data_loader.R

Example Graphs

Hospital bed usage vs Vaccination Percentage of HRR

graph_plotly_point_plot(
  Graph_Vaccination_Hospitalization_Plot("2021-09-23", x_axis = "vacc_complete_percent", y_axis = "covid_bed_usage_ratio")
)
## [1] "Vaccination records loaded from local cache."
## [1] "Hospital bed data loaded from local cache."
## Warning in geom_point(aes(color = population, text = text), size = 1.2):
## Ignoring unknown aesthetics: text
## `geom_smooth()` using formula = 'y ~ x'

Choropleth Graph of Vaccination Stats by HRR

Graph_Vaccination_Rates_Choropleth_By_Hrr_Static("2021/09/24", display_stat = "vacc_complete_percent")
## [1] "Vaccination records loaded from local cache."

graph_plotly_vacc_choropleth(Graph_Vaccination_Rates_Choropleth_By_Hrr("2021/09/24", display_stat = "single_dose_percent", is_scale_adaptive = F))
## [1] "Vaccination records loaded from local cache."
## Warning in layer_sf(geom = GeomSf, data = data, mapping = mapping, stat =
## stat, : Ignoring unknown aesthetics: text

misc

Vaccination Rate by County

##### VACCINATION RATE by county

vaccination_data = get_vaccination_rates_data(date = "2021/09/24")
## [1] "Vaccination records loaded from local cache."
vaccination_data = us_county_shape_data %>% 
  left_join(vaccination_data, by = "fips") 
  
vaccination_data %>% 
  ggplot() +
  geom_sf(aes(fill = series_complete_pop_pct/100)) +
  scale_fill_continuous("Fully Vaccinated", low="red", high="yellow", labels = scales::percent) +
  ggtitle("COVID Vaccination Status", subtitle = "Percentage of county population that is fully vaccinated (from CDC)") +
  my_map_theme()

Bivariate Choropleth

In order to copy the washington post graph, I decided to figure out how to create a bivariate color palette with a simple dataset first, rather than working with covid data.

Make a bivariate color palette

Starting with a color scale generated from Observable HQ, create a color palette by adding each of the colors in the appropriate order. To make sure the order is correct, create a list of (x, y) values. If it is a 16 color palette, then there should be 4 values for each axis. To match the order of colors given by the generator, The order of the x-y pairings should look like (1, 1); (2, 1); (3, 1); (4, 1); (1, 2); (2, 2); (2, 3)… When the colors are plotted with their x and y values, the color pattern will be consistent with those given by the online generator.

library(tidyverse)

#https://observablehq.com/@benjaminadk/bivariate-choropleth-color-generator

colors = c("#d3d3d3", "#a6bddb", "#72a8e3", "#0093e8", "#cea5af", "#a294b5", "#6f83bb", "#0073c0", "#c9748a", "#9f6890", "#6c5c94", "#005198", "#c52f64", "#9c2a68", "#6a266b", "#00216e")

color_palette <- expand.grid(x = 1:4, y = 1:4)

# add a colors column to the data frame using every combination of indexes 1 through 4 for x and y
color_palette$color <- colors[((color_palette$y - 1) * 4) + color_palette$x]
color_palette %>% 
    
  ggplot(aes(x, y)) +
        geom_tile(aes(fill = color)) +
        scale_fill_identity() + 
      coord_fixed()

The x and y sequence is arranged so that the lowest values correlate to light colors So, we need to create and ‘id’ from two different values that correspond to 1-4 for both x and y. I think it is easiest to cut the two values into numbers from 1 to 4 and then join those columns together to get the “xy” id. The colors have already been arranged in this order as can be seen from the color plot above.

Testing

We will first test by mapping to random numbers random x and y values to us counties and then creating an xy id.

I defined the cutpoints so that we have the percentage intervals from 0 - 10, 10 - 30, 30 - 65, 65 - 100

library(albersusa)
usa_states = counties_sf("laea")
## old-style crs object detected; please recreate object with a recent sf::st_crs()
usa_states$height <- sample(0:100, nrow(usa_states), replace = TRUE)
usa_states$width <- sample(0:100, nrow(usa_states), replace = TRUE)

my_cutpoints <- c(-1, 10, 30, 65, 100)
labels <- c(1, 2, 3, 4)

map_data <- usa_states %>% 
  select(height, width) %>% 
  mutate(x = cut(width, breaks = my_cutpoints, labels = labels)) %>% 
  mutate(y = cut(height, breaks = my_cutpoints, labels = labels))

map_data <- map_data %>% unite(id, c("y", "x"), sep = "")


ggplot(map_data) +
  geom_sf(aes(fill = id)) +
  scale_fill_manual(values = color_palette$color) +
  my_map_theme()

Washington Post Map

Graph_Bivariate_Covid_Map("2021-09-23", generate_bivariate_palette(get_wp_palette()))
## [1] "Vaccination records loaded from local cache."
## [1] "Hospital bed data loaded from local cache."

Graph_Bivariate_Covid_Map("2021-09-23", generate_bivariate_palette(get_rb_palette()))
## [1] "Vaccination records loaded from local cache."
## [1] "Hospital bed data loaded from local cache."

LS0tDQp0aXRsZTogIkNvdmlkIEhvc3BpdGFsaXphdGlvbiBhbmQgVmFjY2luYXRpb24gU3R1ZHkiDQphdXRob3I6ICJHcmVnb3J5IE1heW5hcmQiDQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiDQpvdXRwdXQ6IG9wZW5pbnRybzo6bGFiX3JlcG9ydA0KLS0tDQoNCmBgYHtyIGxvYWQtcGFja2FnZXMsIG1lc3NhZ2U9RkFMU0V9DQpsaWJyYXJ5KG9wZW5pbnRybykNCg0Kc291cmNlKCJjb3ZpZF9zdHVkeV9kYXRhX3Bsb3R0ZXIuUiIpDQpgYGANCg0KIyBQcm9qZWN0IERlc2NyaXB0aW9uDQoNClZpZXcgdGhlIFNoaW55IEFwcCBoZXJlOiBodHRwczovL2NpbmRlcnNjcmlwdC5zaGlueWFwcHMuaW8vQ292aWQtVmFjY2luYXRpb24tYW5kLUhvc3BpdGFsLVN0cmFpbi8NCg0KVGhpcyBwcm9qZWN0IGlzIGF2YWlsYWJsZSBvbiBbR2l0SHViXShodHRwczovL3d3dy53YXNoaW5ndG9ucG9zdC5jb20vaGVhbHRoLzIwMjEvMDkvMjMvY292aWQtdmFjY2luYXRpb24taG9zcGl0YWxpemF0aW9uLW1hcC8pDQoNCiMjIEFib3V0DQoNClRoaXMgaXMgYSBzdHVkeSBvZiB2YWNjaW5hdGlvbiByYXRlIGFuZCBob3NwaXRhbCBiZWQgdXNhZ2UgaW4gdGhlIFVuaXRlZCBTdGF0ZXMgaW5zcGlyZWQgYnkgdGhlIFdhc2hpbmd0b24gUG9zdCBhcnRpY2xlIFtNYXBwaW5nIEFtZXJpY2HigJlzIGhvc3BpdGFsaXphdGlvbiBhbmQgdmFjY2luYXRpb24gZGl2aWRlXShodHRwczovL3d3dy53YXNoaW5ndG9ucG9zdC5jb20vaGVhbHRoLzIwMjEvMDkvMjMvY292aWQtdmFjY2luYXRpb24taG9zcGl0YWxpemF0aW9uLW1hcC8pLiBJbiB0aGlzIHByb2plY3Qgd2Ugd2lsbCBiZSByZWNyZWF0aW5nIHRoZSBVU0EgbWFwIGZvdW5kIGluIHRoZSBhcnRpY2xlIGFuZCBhZGRpbmcgaW50ZXJhY3Rpdml0eSBzbyBkaWZmZXJlbnQgZGF0ZXMgYW5kIHZhcmlhYmxlcyBjYW4gYmUgc2VsZWN0ZWQuDQoNCiMjIyMgKipNYXAgQnkgWmFjaCBMZXZpdHQgYW5kIERhbiBLZWF0aW5nOioqDQoNCiFbV2FzaGluZ3RvbiBQb3N0IEJpdmFyaWF0ZSBDaG9yb3BsZXRoIE1hcF0od3d3L3dhc2hpbmd0b24tcG9zdC1tYXAucG5nKQ0KDQojIyBNZXRob2RzDQoNClZhY2NpbmF0aW9uIGRhdGEgaXMgcHJvdmlkZWQgYXQgdGhlIGNvdW50eSBsZXZlbCBieSB0aGUgQ0RDIGFuZCBob3NwaXRhbCBiZWQgdXNhZ2UgZGF0YSBpcyBwcm92aWRlZCBieSBIZWFsdGhEYXRhLmdvdi4gVGhlc2UgdmFyaWFibGVzIGFyZSB2aXN1YWxpemVkIHdpdGggYSBiaXZhcmlhdGUgY2hvcm9wbGV0aCBtYXAgb2YgSG9zcGl0YWwgUmVmZXJyYWwgUmVnaW9ucyBpbiB0aGUgVW5pdGVkIFN0YXRlcy4NCg0KVmFjY2luYXRpb24gUmF0ZSBpcyBkZWZpbmVkIGJ5IGNvdW50eSBhbmQgSFJScyBhcmUgZGVmaW5lZCBieSB6aXAgY29kZS4gV2UgY2FuJ3QgdXNlIGNvdW50eSBkYXRhIHRvIGNhbGN1bGF0ZSB0aGUgdmFjY2luYXRpb24gcmF0ZSBvZiBhbiBIUlIgYmVjYXVzZSB0aGVzZSByZWdpb25zIG92ZXJsYXAuIFppcCBjb2RlcyBpbiBvbmUgSFJSIGNhbiBsaXZlIGluIGRpZmZlcmVudCBjb3VudGllcywgYW5kIFppcCBjb2RlcyBpbiBkaWZmZXJlbnQgY291bnRpZXMgY2FuIGxpdmUgaW4gdGhlIHNhbWUgSFJSLg0KDQoqKldlIG5lZWQgdG8ga25vdyBib3RoIHRoZSBwb3B1bGF0aW9uIG9mIGVhY2ggSFJSIGFuZCB0aGUgdmFjY2luYXRpb24gcmF0ZSBvZiBlYWNoIHBhcnQgb2YgdGhhdCBwb3B1bGF0aW9uLioqDQoNCldlIGRldGVybWluZSB0aGUgdmFjY2luYXRpb24gcmF0ZSBvZiB0aGUgSEhScyBieSBhdmVyYWdpbmcgdGhlIHZhY2NpbmF0aW9uIHJhdGUgKGdpdmVuIGJ5IGNvdW50eSkgb2YgdGhlIHppcCBjb2RlcyBpbiB0aGF0IEhSUi4gVGhlIGluZGl2aWR1YWwgemlwIGNvZGUncyB2YWNjaW5hdGlvbiByYXRlIG5lZWRzIHRvIGJlIHdlaWdodGVkIGJ5IHRoYXQgemlwIGNvZGUncyBwb3B1bGF0aW9uLiBQb3B1bGF0aW9uIGRhdGEgaXMgb2J0YWluZWQgZnJvbSB0aGUgVW5pdGVkIFN0YXRlcyBDZW5zdXMgQnVyZWF1LCB3aGljaCBpcyB1bmZvcnR1bmF0ZWx5IG5vdCBjb3VudGVkIGJ5IHppcCBjb2RlLCBidXQgYnkgYmxvY2tzIHRoYXQgbWFrZSB1cCB0aGUgY29uZ3Jlc3Npb25hbCBkaXN0cmljdHMuIFRvIGVzdGltYXRlIHRoZSBwb3B1bGF0aW9uIG9mIHppcCBjb2Rlcywgd2Ugd2lsbCB1c2UgdGhlIDIwMTAgY2Vuc3VzIHppcCBjb2RlIHRhYnVsYXRpb24gcmVjb3Jkcywgd2hpY2ggYXBwcm94aW1hdGUgdGhlIHppcCBjb2RlcyBpbiB3aGljaCB0aGUgY29uZ3Jlc3Npb25hbCBkaXN0cmljdCBibG9ja3MgbGF5Lg0KDQpUbyBmaW5kIGFsbCB6aXAgY29kZXMgaW4gYW4gSFJSIHdlIHdpbGwgdXNlIGEgWmlwIENvZGUgdG8gSFJSIGNyb3Nzd2Fsay4NCg0KIyBEYXRhIFNvdXJjZXMNCg0KIyMgU3VtbWVyeQ0KDQoxLiBIb3NwaXRhbCBCZWQgVXNhZ2UgaW4gVVNBIC0gcGVyIGhvc3BpdGFsDQoyLiBWYWNjaW5hdGlvbiBSYXRlcyBpbiBVU0EgLSBwZXIgY291bnR5DQozLiBQb3B1bGF0aW9uIENlbnN1cyBpbiBVU0EgLSBwZXIgemN0YQ0KNC4gSFJSIEdlb2dyYXBoeSBpbiBVU0EgLSBwZXIgSFJSIG51bWJlcg0KNS4gQ291bnR5IEdlb2dyYXBoeSBpbiBVU0EgLSBwZXIgY291bnR5DQo2LiBDcm9zc3dhbGsgZm9yIFppcCBDb2RlIGFuZCBIUlIgbnVtYmVyDQoNCkFueXRpbWUgYSBuZXcgZmlsZSBpcyBkb3dubG9hZGVkLCB0aGF0IGZpbGUgaXMgY2FjaGVkIGluIHRoZSAiY2FjaGVkLWRhdGEiIGZvbGRlci4gQW55dGltZSBhIHJlcXVlc3QgaXMgbWFkZSANCmZvciBhIGRhdGFzZXQgYnkgZGF0ZSwgdGhpcyBmb2xkZXIgaXMgY2hlY2tlZCBmaXJzdC4NCg0KIyMgTG9jYWwgRGF0YSBDYWNoaW5nDQoNCkFsbCBkYXRhIHNvdXJjZXMgYXJlIGRvd25sb2FkZWQgZnJvbSB0aGUgaW50ZXJuZXQuIE9ubHkgdGhlIG5lZWRlZCBwb3J0aW9ucyBvZiB0aGUgZGF0YXNldHMgYXJlIHJlcXVlc3RlZCBmcm9tIHRoZSBjb3JyZXNwb25kaW5nIGVuZHBvaW50cy4gDQpCZWZvcmUgZG93bmxvYWRpbmcsIHRoaXMgYXBwbGljYXRpb24gZmlyc3QgY2hlY2tzIGlmIHRoZSBkYXRhc2V0IGhhcyBhbHJlYWR5IGJlZW4gZG93bmxvYWRlZCBpbiBhIGxvY2FsIGNhY2hlIA0KZmlsZS4gV2hlbmV2ZXIgYSBuZXcgcG9ydGlvbiBvZiBhIGRhdGFzZXQgaXMgZG93bmxvYWRlZCwgaXQgaXMgc2F2ZWQgdG8gdGhlIGNhY2hlIGZvbGRlciBsb2NhbCB0byB0aGUgYXBwbGljYXRpb24gDQpmb2xkZXIuDQoNCiMjIFZhY2NpbmF0aW9uIFJhdGVzIERhdGEgcGVyIFVTIENvdW50eQ0KDQpWYWNjaW5hdGlvbiByYXRlcyBhcmUgb2J0YWluZWQgZnJvbSB0aGUgQ0RDOiBodHRwczovL2RhdGEuY2RjLmdvdi9WYWNjaW5hdGlvbnMvQ09WSUQtMTktVmFjY2luYXRpb25zLWluLXRoZS1Vbml0ZWQtU3RhdGVzLUNvdW50eS84eGt4LWFtcWguIA0KVGhpcyBkYXRhc2V0IGlzIGxhcmdlLCBzbyBpbnN0ZWFkIG9mIGRvd25sb2FkaW5nIHRoZSB3aG9sZSBkYXRhc2V0LCB0aGlzIGFwcGxpY2F0aW9uIGFjY2Vzc2VzIGl0IHRocm91Z2ggdGhlIFNPREEgQVBJIGFuZCByZXRyaWV2ZXMgb25seSB0aGUgDQpyZWxldmFudCByb3dzIGFuZCBjb2x1bW5zLg0KDQpUaGUgIkNPVklELTE5IFZhY2NpbmF0aW9ucyBpbiB0aGUgVW5pdGVkIFN0YXRlcyxDb3VudHkiIGRhdGEgcHJvdmlkZXMgY291bnRzIGFuZCBwZXJjZW50YWdlcyBvZiBwZW9wbGUgd2hvIGhhdmUgYmVlbiANCnZhY2NpbmF0ZWQgaW4gZWFjaCBjb3VudHkgb2YgdGhlIFVuaXRlZCBTdGF0ZXMuDQoNClRoZSB2YXJpYWJsZXMgcmV0cmlldmVkIGFyZToNCg0KKiAqKmZpcHMqKg0KKiAqKnNlcmllc19jb21wbGV0ZV9wb3BfcGN0Kio6ICJQZXJjZW50IG9mIHBlb3BsZSB3aG8gaGF2ZSBjb21wbGV0ZWQgYSBwcmltYXJ5IHNlcmllcyAoaGF2ZSBzZWNvbmQgZG9zZSBvZiBhIHR3by1kb3NlIHZhY2NpbmUgb3Igb25lIGRvc2Ugb2YgYSBzaW5nbGUtZG9zZSB2YWNjaW5lKSBiYXNlZCBvbiB0aGUganVyaXNkaWN0aW9uIGFuZCBjb3VudHkgd2hlcmUgdmFjY2luZSByZWNpcGllbnQgbGl2ZXMuIg0KKiAqKmFkbWluaXN0ZXJlZF9kb3NlMV9wb3BfcGN0Kio6ICJQZXJjZW50IG9mIFRvdGFsIFBvcCB3aXRoIGF0IGxlYXN0IG9uZSBEb3NlIGJ5IFN0YXRlIG9mIFJlc2lkZW5jZSINCiogKipib29zdGVyX2Rvc2VzX3ZheF9wY3QqKjogIlBlcmNlbnQgb2YgcGVvcGxlIHdobyBjb21wbGV0ZWQgYSBwcmltYXJ5IHNlcmllcyBhbmQgaGF2ZSByZWNlaXZlZCBhIGJvb3N0ZXIgKG9yIGFkZGl0aW9uYWwpIGRvc2UuIg0KDQojIyBIb3NwaXRhbCBDYXBhY2l0eSBEYXRhIG9mIFVTQSBwZXIgSG9zcGl0YWwNCg0KSG9zcGl0YWwgYmVkIHVzYWdlIGNvdW50cyBpcyBvYnRhaW5lZCBmcm9tIEhlYWx0aERhdGEuZ292OiBodHRwczovL2hlYWx0aGRhdGEuZ292L0hvc3BpdGFsL0NPVklELTE5LVJlcG9ydGVkLVBhdGllbnQtSW1wYWN0LWFuZC1Ib3NwaXRhbC1DYXBhL2FuYWctY3c3dS4gDQpUaGlzIGRhdGFzZXQgaXMgbGFyZ2UsIHNvIGluc3RlYWQgb2YgZG93bmxvYWRpbmcgdGhlIHdob2xlIGRhdGFzZXQsIHRoaXMgYXBwbGljYXRpb24gYWNjZXNzZXMgaXQgdGhyb3VnaCB0aGUgU09EQSBBUEkgYW5kIHJldHJpZXZlcyBvbmx5IHRoZSANCnJlbGV2YW50IHJvd3MgYW5kIGNvbHVtbnMuDQoNClRoZSAiQ09WSUQtMTkgUmVwb3J0ZWQgUGF0aWVudCBJbXBhY3QgYW5kIEhvc3BpdGFsIENhcGFjaXR5IGJ5IEZhY2lsaXR5IiBkYXRhIHByb3ZpZGVzIGNvdW50cyBvbiBob3NwaXRhbCBiZWQgdXRpbGl6YXRpb24gdGhhdCBpcyBhZ2dyZWdhdGVkIHdlZWtseS4NCg0KVGhlIHZhcmlhYmxlcyByZXRyaWV2ZWQgYXJlOg0KDQoqICoqSG9zcGl0YWxfbmFtZSoqIGFuZCAqKmZpcHNfY29kZSoqDQoqICoqaW5wYXRpZW50X2JlZHNfN19kYXlfYXZnKio6ICJBdmVyYWdlIG51bWJlciBvZiB0b3RhbCBudW1iZXIgb2Ygc3RhZmZlZCBpbnBhdGllbnQgYmVkcyBpbiB5b3VyIGhvc3BpdGFsIGluY2x1ZGluZyBhbGwgb3ZlcmZsb3csIG9ic2VydmF0aW9uLCBhbmQgYWN0aXZlIHN1cmdlL2V4cGFuc2lvbiBiZWRzIHVzZWQgZm9yIGlucGF0aWVudHMgKGluY2x1ZGluZyBhbGwgSUNVIGJlZHMpIHJlcG9ydGVkIGluIHRoZSA3LWRheSBwZXJpb2QuIg0KKiAqKmlucGF0aWVudF9iZWRzX3VzZWRfN19kYXlfYXZnKio6ICJBdmVyYWdlIG9mIHRvdGFsIG51bWJlciBvZiBzdGFmZmVkIGlucGF0aWVudCBiZWRzIHRoYXQgYXJlIG9jY3VwaWVkIHJlcG9ydGVkIGR1cmluZyB0aGUgNy1kYXkgcGVyaW9kLiINCiogKippbnBhdGllbnRfYmVkc191c2VkX2NvdmlkXzdfZGF5X2F2ZyoqOiAiQXZlcmFnZSBvZiByZXBvcnRlZCBwYXRpZW50cyBjdXJyZW50bHkgaG9zcGl0YWxpemVkIGluIGFuIGlucGF0aWVudCBiZWQgd2hvIGhhdmUgc3VzcGVjdGVkIG9yIGNvbmZpcm1lZCBDT1ZJRC0xOSByZXBvcnRlZCBkdXJpbmcgdGhlIDctZGF5IHBlcmlvZC4iDQoNCipJbnBhdGllbnQgYmVkIGNvdW50cyBhcmUgdXNlZCBpbnN0ZWFkIG9mIHRvdGFsIGJlZCBjb3VudHMgYmVjYXVzZSBtYW55IGhvc3BpdGFscyB0aGF0IG9ubHkgaGF2ZSBpbnBhdGllbnQgYmVkIGRhdGEgZG8gbm90IGluY2x1ZGUgZGF0YSBmb3IgaW5wYXRpZW50IGFuZCBvdXRwYXRpZW50IHRvdGFscy4qDQoNCg0KIyMgUG9wdWxhdGlvbiBDZW5zdXMgRGF0YQ0KDQpQb3B1bGF0aW9uIGRhdGEgaXMgb2J0YWluZWQgZnJvbSB0aGUgVW5pdGVkIFN0YXRlcyBDZW5zdXMgQnVyZWF1IHVzaW5nIHRoZWlyIDIwMTAgWkNUQSB0byBDb3VudHkgUmVsYXRpb25zaGlwIEZpbGUgKHpjdGFfY291bnR5X3JlbF8xMC50eHQpDQoNCmh0dHBzOi8vd3d3LmNlbnN1cy5nb3YvZ2VvZ3JhcGhpZXMvcmVmZXJlbmNlLWZpbGVzL3RpbWUtc2VyaWVzL2dlby9yZWxhdGlvbnNoaXAtZmlsZXMuMjAxMC5odG1sI3Bhcl90ZXh0aW1hZ2VfNjc0MTczNjIyDQoNCmRvd25sb2FkOiBodHRwczovL3d3dzIuY2Vuc3VzLmdvdi9nZW8vZG9jcy9tYXBzLWRhdGEvZGF0YS9yZWwvemN0YV9jb3VudHlfcmVsXzEwLnR4dA0KY29sdW1uIGRlc2NyaXB0aW9uczogaHR0cHM6Ly93d3cuY2Vuc3VzLmdvdi9wcm9ncmFtcy1zdXJ2ZXlzL2dlb2dyYXBoeS90ZWNobmljYWwtZG9jdW1lbnRhdGlvbi9yZWNvcmRzLWxheW91dC8yMDEwLXpjdGEtcmVjb3JkLWxheW91dC5odG1sI3Bhcl90ZXh0aW1hZ2VfMA0KDQojIyBHZW9ncmFwaGljIFNoYXBlIERhdGENCg0KIyMjIyAqVVMgQ291bnR5IFNoYXBlIERhdGEqDQoNCkNvdW50eSBzaGFwZSBkYXRhIGlzIG9idGFpbmVkIGZyb20gdGhlIFIgcGFja2FnZSBgQWxiZXJzdXNhYC4NCg0KIyMjIyAqSG9zcGl0YWwgUmVmZXJyYWwgUmVnaW9uIFNoYXBlIERhdGEqDQoNClNoYXBlIGRhdGEgZm9yIFVTQSBIUlIgcmVnaW9ucyBhcmUgZG93bmxvYWRlZCBmcm9tIGFyY2dpcy5jb20gdXNpbmcgdGhlaXIgIkZlYXR1cmVTZXJ2ZXIiIFJFU1QgYXBpLg0KDQpodHRwczovL3d3dy5hcmNnaXMuY29tL2hvbWUvaXRlbS5odG1sP2lkPTQ2YmY2NzkwYzRlMDQ1NWU5Mzc5ZWU5NzY5YjFhNWFiDQoNCg0KIyMgQ3Jvc3N3YWxrIERhdGENCg0KIyMjIyAqSFJSIG51bWJlciB0byB6aXAgY29kZSB0cmFuc2xhdGlvbiAoMjAxOSkqDQoNClRoaXMgY3Jvc3N3YWxrIGlzIG9idGFpbmVkIGZyb20gRGFydG1vdXRoIEF0bGFzIGFzIGEgemlwIGZpbGUuDQoNCmh0dHBzOi8vZGF0YS5kYXJ0bW91dGhhdGxhcy5vcmcvc3VwcGxlbWVudGFsLyNjcm9zc3dhbGtzDQoNCg0KIyMgQ2FsY3VsYXRlZCBEYXRhDQoNCiMjIyBSYXRpb3MgZm9yIEhvc3BpdGFsIEJlZCBVc2FnZSBwZXIgSG9zcGl0YWwNCg0KV2UgYWxzbyBjYWxjdWxhdGU6IA0KDQoqICoqYmVkX3VzYWdlX3JhdGlvKio6IFRoZSByYXRpbyBvZiBob3NwaXRhbCBiZWRzIHVzZWQgb3V0IG9mIDEwMCBiZWRzIChpbnBhdGllbnQpDQoqICoqY292aWRfYmVkX3VzYWdlX3JhdGlvKio6IFRoZSByYXRpbyBvZiBob3NwaXRhbCBiZWRzIHVzZWQgYnkgY292aWQgcGF0aWVudHMgb3V0IG9mIDEwMCBiZWRzIChpbnBhdGllbnQpDQoqICoqY292aWRfYmVkX3VzYWdlX3RvdGFsX2JlZF91c2FnZV9yYXRpbyoqOiBUaGUgcmF0aW8gb2YgaG9zcGl0YWwgYmVkcyB1c2VkIGJ5IGNvdmlkIHBhdGllbnRzIG91dCBvZiBhbGwgdXNlZCBiZWRzIChpbnBhdGllbnQpDQoNCiMjIyBBdmVyYWdlIEJlZCBSYXRpb3MgcGVyIFJlZ2lvbg0KDQpUaGUgaG9zcGl0YWwgYmVkIGRhdGFzZXQgY29udGFpbnMgcmVjb3JkcyBmb3IgZWFjaCBob3NwaXRhbC4gRm9yIG1hcHBpbmcsIGVhY2ggbWFwIHJlZ2lvbiB3aWxsIG5lZWQgdG8gcmVwcmVzZW50IHRoZSBhdmVyYWdlIA0Kb2YgYWxsIGhvc3BpdGFscyBwcmVzZW50IGluIHRoYXQgcmVnaW9uLg0KDQojIyMgSG9zcGl0YWwgUmVmZXJyYWwgUmVnaW9uIHBvcHVsYXRpb24NCg0KQSBkYXRhc2V0IGlzIG5lZWRlZCB0aGF0IHRyYWNrcyB0aGUgcG9wdWxhdGlvbiBwZXJjZW50YWdlcyBvZiBlYWNoIFpDVEEgaW4gZWFjaCBjb3VudHkgZm9yIHRoZSBmdW5jdGlvbiB0aGF0IGNhbGN1bGF0ZXMgdmFjY2luYXRpb24gcmF0ZXMgb2YgSFJScy4gVGhpcyBkYXRhc2V0IGlzIGdlbmVyYXRlZCBieSBqb2luaW5nIFpDVEEgcG9wdWxhdGlvbnMgd2l0aCB6aXAgY29kZXMuIFRoZSBwYXJ0aWFsIHBvcHVsYXRpb25zIG9mIFpDVEFzIGluIGEgZ2l2ZW4gemlwIGNvZGUgKGR1ZSB0byBvdmVybGFwcGluZyB6Y3RhIGFuZCB6aXBjb2RlIHJlZ2lvbnMpIGlzIGFjY291bnRlZCBmb3IuDQoNClByb2Nlc3NlZCBieSBTb3VyY2U6IGBjb3ZpZF9zdHVkeV9kYXRhX3dyYW5nbGVyLlJgLCB3aGljaCBjcmVhdGVzIGZ1bmN0aW9ucyBmb3IgZ2VuZXJhdGluZyBncmFwaC1hYmxlIGRhdGFzZXRzDQoNCiMjIyBDYWxjdWxhdGUgSEhSIHZhY2NpbmF0aW9uIHJhdGUNCg0KKipXZSBhcmUgdXNpbmcgcG9wdWxhdGlvbiBkYXRhIGZvciB6aXAgY29kZSB0YWJ1bGF0aW9uIGFyZWFzIGJlY2F1c2Ugd2UgZG9uJ3QgaGF2ZSBjb3VudHMgZm9yIHppcCBjb2Rlcy4gWkNUQXMgd2lsbCBiZSBvdXIgcHJveHkgZm9yIHppcCBjb2Rlcy4gQmVjYXVzZSB6Y3RhJ3MgZG9uJ3QgbGluZSB1cCBleGFjdGx5IHdpdGggemlwIGNvZGVzLCB0aGUgc2FtZSB6Y3RhIGNhbiBiZSBwYXJ0IG9mIG1vcmUgdGhhbiBvbmUgY291bnR5LiBUaGF0IGlzIHdoeSBpdCBpcyBpbXBvcnRhbnQgdG8ga25vdyBob3cgbXVjaCBvZiB0aGUgWkNUQSdzIHBvcHVsYXRpb24gaXMgaW4gZWFjaCBjb3VudHkuKioNCg0KKlRoZSBQcm9jZXNzOioNCg0KMS4gSm9pbiB0aGUgdmFjY2luYXRpb24gZGF0YSBmb3IgMjAyMS8wOS8yNCB3aXRoIHBvcHVsYXRpb24gemlwIGNvZGUgZGF0YSBieSBjb3VudHkuDQogICogKk5vdyB3ZSBrbm93IHRoZSB2YWNjaW5hdGlvbiByYXRlIG9mIGVhY2ggemlwIGNvZGUqDQoyLiBKb2luIHdpdGggdGhlIHppcCBoaHIgY3Jvc3N3YWxrIGJ5IHppcCBjb2RlIHRvIHpjdGEuDQogICogKk5vdyB3ZSBrbm93IHRoZSBocnIgZWFjaCB6aXAgY29kZSBpcyBpbiBhbG9uZyB3aXRoIHRoYXQgemlwIGNvZGUncyB2YWNjaW5hdGlvbiByYXRlKg0KMy4gSm9pbiB3aXRoIHRoZSBrbm93biBwb3B1bGF0aW9uIGNvdW50cyBvZiB6aXAgY29kZXMgYnkgemlwIGNvZGUuDQogICogKk5vdyB3ZSBrbm93IHRoZSBzbGljZSBvZiBwb3B1bGF0aW9uIG9mIGEgemlwIGNvZGUgaW5zaWRlIGVhY2ggY291bnR5IGFsb25nIHdpdGggdGhhdCBwb3B1bGF0aW9uIHNsaWNlJ3MgdmFjY2luYXRpb24gcmF0ZSoNCjQuIENhbGN1bGF0ZSBudW1iZXIgb2YgdmFjY2luYXRlZCBwZW9wbGUgaW4gZWFjaCB6aXAgY29kZSdzIHBvcHVsYXRpb24gc2xpY2UgaW5zaWRlIGVhY2ggY291bnR5IGZvciBlYWNoIGhycg0KNS4gRGl2aWRlIHRoZSBudW1iZXIgb2YgdmFjY2luYXRlZCBpbiB6aXAgc2xpY2UgYnkgdGhlIHRvdGFsIGhyciBwb3B1bGF0aW9uIHRoYXQgemlwIGNvZGUgaXMgaW4uDQogICogKlRoaXMgZ2l2ZXMgdXMgdGhlICJzbGljZSB2YWNjaW5hdGlvbiBwZXJjZW50YWdlIiB0aGF0IGNvbnRyaWJ1dGVzIHRvIHRoZSB2YWNjaW5hdGlvbiBwZXJjZW50YWdlIG9mIHRoZSBocnIqDQo2LiBBZGQgdXAgYWxsIG9mIHRoZSBzbGljZWQgdmFjY2luYXRpb24gcGVyY2VudGFnZXMgZm9yIGVhY2ggaHJyLg0KDQoqKkZ1bmN0aW9uIGRlZmluZWQgaW4gU291cmNlOiBgY292aWRfc3R1ZHlfZGF0YV93cmFuZ2xlci5SIDo6IGNhbGN1bGF0ZV9ocnJfdmFjY2luYXRpb25fcmF0ZXMoZGF0ZSlgKioNCg0KDQojIyBEYXRhIENsZWFuaW5nDQoNCiMjIyBUZXhhcw0KVGhlcmUgaXMgYSBsb3Qgb2YgbWlzc2luZyBkYXRhIGZvciBib3RoIGhvc3BpdGFsIGJlZCB1c2FnZSBhbmQgdmFjY2luYXRpb24gcmF0ZXMuIFRFWEFTIHJlY29yZHMgDQpiZWZvcmUgMjAyMS0xMC0yMiBhcmUgcmVtb3ZlZC4gQmVmb3JlIDIwMjEtMTAtMjIsIFRleGFzIGhhZCBwcm9ibGVtcyB3aXRoIHRoZWlyIHJlY29yZGVkIHZhY2NpbmF0aW9uIA0KcmF0ZXMgYW5kIHRoZXkgYXJlIHJlY29yZGVkIGFzICcwJyBpbiB0aGUgZGF0YWZyYW1lcy4gVGhlc2UgcmVjb3JkcyBhcmUgcmVtb3ZlZCBzbyB0aGV5IGRvbid0IA0KdGhyb3cgb2ZmIHRoZSBwZXJjZW50YWdlcyBvZiBncmFwaGVkIHN0YXRzIGFuZCBhdXRvbWF0aWMgcmFuZ2Ugc2NhbGluZyBvZiB0aGUgZ3JhcGhzLg0KDQpJbmZvcm1hdGlvbjogaHR0cHM6Ly93d3cudGV4YXN0cmlidW5lLm9yZy8yMDIxLzAxLzIwL3RleGFzLWNvcm9uYXZpcnVzLXZhY2NpbmUtZGF0YS8NCg0KIyMjIE5vbnNlbnNpY2FsIERhdGENClJlcGxhY2UgMCUgc2luZ2xlIGRvc2UgcGVyY2VudGFnZXMgd2l0aCBOQSB3aGVyZSBhcHByb3ByaWF0ZQ0KQWxzbywgTWFueSByZWNvcmRzIGZvciB0aGUgc2luZ2xlIGRvc2UgPT0gMCB3aGlsZSBzZXJpZXMgY29tcGxldGUgaXMgPiAwLiBUaGlzIGlzbid0DQpwb3NzaWJsZSBzbyB0aGVzZSB2YWx1ZXMgcHJvYmFibHkgd2VyZSBub3QgcmVjb3JkZWQuIFJlcGxhY2UgdGhlbSB3aXRoIE5BIHNvIHRoYXQNCmZ1dHVyZSBkYXRhIHdyYW5nbGluZyBpZ25vcmVzIHRob3NlIHZhbHVlcyBpbiBjYWxjdWxhdGlvbnMuDQoNCkJ5IDIwMjEtMDItMDEgYWxsIEhIUiByZWdpb24gc3RhdGVzIGhhdmUgc29tZSBzaW5nbGUgZG9zZSBwZXJjZW50YWdlIFRYIGlzIHRoZSBvbmx5IHN0YXRlIA0KdGhhdCBoYXMgMCUsIHdoaWNoIHdlIGRvbid0IGhhdmUgY29sbGVjdGVkIGRhdGEgZm9yLiBBZnRlciAyMDIxLTAxLTMxLCBhbGwgZW50cmllcyANCnRoYXQgaGF2ZSAwJSBzaW5nbGUgZG9zZSBhcmUgcmVtb3ZlZC4gKEFsbCBlbnRyaWVzIHRoYXQgYXJlIGVpdGhlciBiZWZvcmUgMjAyMS0wMi0wMSANCm9yIGhhdmUgc29tZSBwZXJjZW50YWdlIG9mIHNpbmdsZSBkb3NlIGFyZSBrZXB0KQ0KDQoNCiMgU291cmNlIEZpbGVzDQoNClNvdXJjZSBGaWxlICAgICAgICAgICAgICAgICAgIHwgIERlc2NyaXB0aW9uDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmBjb3ZpZF9zdHVkeV9kYXRhX2xvYWRlci5SYCAgIHwgIGxvYWRzIGFsbCByZXF1aXJlZCBkYXRhDQpgY292aWRfc3R1ZHlfZGF0YV93cmFuZ2xlci5SYCB8ICBmdW5jdGlvbnMgZm9yIGdlbmVyYXRpbmcgZ3JhcGgtYWJsZSBkYXRhc2V0cw0KYGNvdmlkX3N0dWR5X2RhdGFfcGxvdHRlci5SYCAgfCAgZnVuY3Rpb25zIGZvciBnZW5lcmF0aW5nIGdncGxvdCBhbmQgZ2dwbG90bHkgZ3JhcGhzDQpgYXBwLlJgICAgICAgICAgICAgICAgICAgICAgICB8ICBzaGlueSBhcHANCg0KU291cmNlIERlcGVuZGVuY3k6DQoNCmBhcHAuUmAgLS0+IGBjb3ZpZF9zdHVkeV9kYXRhX3Bsb3R0ZXIuUmAgLS0+IGBjb3ZpZF9zdHVkeV9kYXRhX3dyYW5nbGVyLlJgIC0tPiBgY292aWRfc3R1ZHlfZGF0YV9sb2FkZXIuUmANCg0KDQojIEV4YW1wbGUgR3JhcGhzDQoNCiMjIyBIb3NwaXRhbCBiZWQgdXNhZ2UgdnMgVmFjY2luYXRpb24gUGVyY2VudGFnZSBvZiBIUlINCmBgYHtyfQ0KZ3JhcGhfcGxvdGx5X3BvaW50X3Bsb3QoDQogIEdyYXBoX1ZhY2NpbmF0aW9uX0hvc3BpdGFsaXphdGlvbl9QbG90KCIyMDIxLTA5LTIzIiwgeF9heGlzID0gInZhY2NfY29tcGxldGVfcGVyY2VudCIsIHlfYXhpcyA9ICJjb3ZpZF9iZWRfdXNhZ2VfcmF0aW8iKQ0KKQ0KYGBgDQoNCg0KDQojIyMgQ2hvcm9wbGV0aCBHcmFwaCBvZiBWYWNjaW5hdGlvbiBTdGF0cyBieSBIUlINCg0KYGBge3J9DQpHcmFwaF9WYWNjaW5hdGlvbl9SYXRlc19DaG9yb3BsZXRoX0J5X0hycl9TdGF0aWMoIjIwMjEvMDkvMjQiLCBkaXNwbGF5X3N0YXQgPSAidmFjY19jb21wbGV0ZV9wZXJjZW50IikNCmdyYXBoX3Bsb3RseV92YWNjX2Nob3JvcGxldGgoR3JhcGhfVmFjY2luYXRpb25fUmF0ZXNfQ2hvcm9wbGV0aF9CeV9IcnIoIjIwMjEvMDkvMjQiLCBkaXNwbGF5X3N0YXQgPSAic2luZ2xlX2Rvc2VfcGVyY2VudCIsIGlzX3NjYWxlX2FkYXB0aXZlID0gRikpDQpgYGANCg0KDQojIyMgbWlzYw0KDQpWYWNjaW5hdGlvbiBSYXRlIGJ5IENvdW50eQ0KDQpgYGB7cn0NCiMjIyMjIFZBQ0NJTkFUSU9OIFJBVEUgYnkgY291bnR5DQoNCnZhY2NpbmF0aW9uX2RhdGEgPSBnZXRfdmFjY2luYXRpb25fcmF0ZXNfZGF0YShkYXRlID0gIjIwMjEvMDkvMjQiKQ0KDQp2YWNjaW5hdGlvbl9kYXRhID0gdXNfY291bnR5X3NoYXBlX2RhdGEgJT4lIA0KICBsZWZ0X2pvaW4odmFjY2luYXRpb25fZGF0YSwgYnkgPSAiZmlwcyIpIA0KICANCnZhY2NpbmF0aW9uX2RhdGEgJT4lIA0KICBnZ3Bsb3QoKSArDQogIGdlb21fc2YoYWVzKGZpbGwgPSBzZXJpZXNfY29tcGxldGVfcG9wX3BjdC8xMDApKSArDQogIHNjYWxlX2ZpbGxfY29udGludW91cygiRnVsbHkgVmFjY2luYXRlZCIsIGxvdz0icmVkIiwgaGlnaD0ieWVsbG93IiwgbGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KSArDQogIGdndGl0bGUoIkNPVklEIFZhY2NpbmF0aW9uIFN0YXR1cyIsIHN1YnRpdGxlID0gIlBlcmNlbnRhZ2Ugb2YgY291bnR5IHBvcHVsYXRpb24gdGhhdCBpcyBmdWxseSB2YWNjaW5hdGVkIChmcm9tIENEQykiKSArDQogIG15X21hcF90aGVtZSgpDQoNCmBgYA0KDQoNCiMgQml2YXJpYXRlIENob3JvcGxldGgNCg0KSW4gb3JkZXIgdG8gY29weSB0aGUgd2FzaGluZ3RvbiBwb3N0IGdyYXBoLCBJIGRlY2lkZWQgdG8gZmlndXJlIG91dCBob3cgdG8gY3JlYXRlIGEgYml2YXJpYXRlIA0KY29sb3IgcGFsZXR0ZSB3aXRoIGEgc2ltcGxlIGRhdGFzZXQgZmlyc3QsIHJhdGhlciB0aGFuIHdvcmtpbmcgd2l0aCBjb3ZpZCBkYXRhLg0KDQoNCiMjIyBNYWtlIGEgYml2YXJpYXRlIGNvbG9yIHBhbGV0dGUNCg0KU3RhcnRpbmcgd2l0aCBhIGNvbG9yIHNjYWxlIGdlbmVyYXRlZCBmcm9tIFtPYnNlcnZhYmxlIEhRXShodHRwczovL29ic2VydmFibGVocS5jb20vQGJlbmphbWluYWRrL2JpdmFyaWF0ZS1jaG9yb3BsZXRoLWNvbG9yLWdlbmVyYXRvciksIA0KY3JlYXRlIGEgY29sb3IgcGFsZXR0ZSBieSBhZGRpbmcgZWFjaCBvZiB0aGUgY29sb3JzIGluIHRoZSBhcHByb3ByaWF0ZSBvcmRlci4gVG8gbWFrZSBzdXJlIHRoZSBvcmRlciBpcyBjb3JyZWN0LCBjcmVhdGUgYSBsaXN0IG9mICh4LCB5KSB2YWx1ZXMuIElmIGl0IA0KaXMgYSAxNiBjb2xvciBwYWxldHRlLCB0aGVuIHRoZXJlIHNob3VsZCBiZSA0IHZhbHVlcyBmb3IgZWFjaCBheGlzLiBUbyBtYXRjaCB0aGUgb3JkZXIgb2YgY29sb3JzIGdpdmVuIGJ5IHRoZSBnZW5lcmF0b3IsIFRoZSBvcmRlciBvZiB0aGUgeC15IHBhaXJpbmdzIA0Kc2hvdWxkIGxvb2sgbGlrZSAoMSwgMSk7ICgyLCAxKTsgKDMsIDEpOyAoNCwgMSk7ICgxLCAyKTsgKDIsIDIpOyAoMiwgMykuLi4gV2hlbiB0aGUgY29sb3JzIGFyZSBwbG90dGVkIHdpdGggdGhlaXIgeCBhbmQgeSB2YWx1ZXMsIHRoZSBjb2xvciBwYXR0ZXJuIA0Kd2lsbCBiZSBjb25zaXN0ZW50IHdpdGggdGhvc2UgZ2l2ZW4gYnkgdGhlIG9ubGluZSBnZW5lcmF0b3IuDQoNCg0KYGBge3J9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCg0KI2h0dHBzOi8vb2JzZXJ2YWJsZWhxLmNvbS9AYmVuamFtaW5hZGsvYml2YXJpYXRlLWNob3JvcGxldGgtY29sb3ItZ2VuZXJhdG9yDQoNCmNvbG9ycyA9IGMoIiNkM2QzZDMiLCAiI2E2YmRkYiIsICIjNzJhOGUzIiwgIiMwMDkzZTgiLCAiI2NlYTVhZiIsICIjYTI5NGI1IiwgIiM2ZjgzYmIiLCAiIzAwNzNjMCIsICIjYzk3NDhhIiwgIiM5ZjY4OTAiLCAiIzZjNWM5NCIsICIjMDA1MTk4IiwgIiNjNTJmNjQiLCAiIzljMmE2OCIsICIjNmEyNjZiIiwgIiMwMDIxNmUiKQ0KDQpjb2xvcl9wYWxldHRlIDwtIGV4cGFuZC5ncmlkKHggPSAxOjQsIHkgPSAxOjQpDQoNCiMgYWRkIGEgY29sb3JzIGNvbHVtbiB0byB0aGUgZGF0YSBmcmFtZSB1c2luZyBldmVyeSBjb21iaW5hdGlvbiBvZiBpbmRleGVzIDEgdGhyb3VnaCA0IGZvciB4IGFuZCB5DQpjb2xvcl9wYWxldHRlJGNvbG9yIDwtIGNvbG9yc1soKGNvbG9yX3BhbGV0dGUkeSAtIDEpICogNCkgKyBjb2xvcl9wYWxldHRlJHhdDQpjb2xvcl9wYWxldHRlICU+JSANCiAgICANCiAgZ2dwbG90KGFlcyh4LCB5KSkgKw0KICAgIAlnZW9tX3RpbGUoYWVzKGZpbGwgPSBjb2xvcikpICsNCiAgICAJc2NhbGVfZmlsbF9pZGVudGl0eSgpICsgDQogICAgICBjb29yZF9maXhlZCgpDQpgYGANCg0KVGhlIHggYW5kIHkgc2VxdWVuY2UgaXMgYXJyYW5nZWQgc28gdGhhdCB0aGUgbG93ZXN0IHZhbHVlcyBjb3JyZWxhdGUgdG8gbGlnaHQgY29sb3JzDQpTbywgd2UgbmVlZCB0byBjcmVhdGUgYW5kICdpZCcgZnJvbSB0d28gZGlmZmVyZW50IHZhbHVlcyB0aGF0IGNvcnJlc3BvbmQgdG8gMS00ICBmb3IgYm90aCB4IGFuZCB5Lg0KSSB0aGluayBpdCBpcyBlYXNpZXN0IHRvIGN1dCB0aGUgdHdvIHZhbHVlcyBpbnRvIG51bWJlcnMgZnJvbSAxIHRvIDQgYW5kIHRoZW4gam9pbiB0aG9zZSBjb2x1bW5zIHRvZ2V0aGVyIA0KdG8gZ2V0IHRoZSAieHkiIGlkLiAgVGhlIGNvbG9ycyBoYXZlIGFscmVhZHkgYmVlbiBhcnJhbmdlZCBpbiB0aGlzIG9yZGVyIGFzIGNhbiBiZSBzZWVuIGZyb20gdGhlIA0KY29sb3IgcGxvdCBhYm92ZS4NCg0KIyMjIyBUZXN0aW5nDQoNCldlIHdpbGwgZmlyc3QgdGVzdCBieSBtYXBwaW5nIHRvIHJhbmRvbSBudW1iZXJzIHJhbmRvbSB4IGFuZCB5IHZhbHVlcyB0byB1cyBjb3VudGllcyBhbmQgdGhlbiBjcmVhdGluZyBhbiB4eSBpZC4NCg0KSSBkZWZpbmVkIHRoZSBjdXRwb2ludHMgc28gdGhhdCB3ZSBoYXZlIHRoZSBwZXJjZW50YWdlIGludGVydmFscyBmcm9tIDAgLSAxMCwgMTAgLSAzMCwgMzAgLSA2NSwgNjUgLSAxMDANCg0KDQpgYGB7cn0NCmxpYnJhcnkoYWxiZXJzdXNhKQ0KdXNhX3N0YXRlcyA9IGNvdW50aWVzX3NmKCJsYWVhIikNCg0KdXNhX3N0YXRlcyRoZWlnaHQgPC0gc2FtcGxlKDA6MTAwLCBucm93KHVzYV9zdGF0ZXMpLCByZXBsYWNlID0gVFJVRSkNCnVzYV9zdGF0ZXMkd2lkdGggPC0gc2FtcGxlKDA6MTAwLCBucm93KHVzYV9zdGF0ZXMpLCByZXBsYWNlID0gVFJVRSkNCg0KbXlfY3V0cG9pbnRzIDwtIGMoLTEsIDEwLCAzMCwgNjUsIDEwMCkNCmxhYmVscyA8LSBjKDEsIDIsIDMsIDQpDQoNCm1hcF9kYXRhIDwtIHVzYV9zdGF0ZXMgJT4lIA0KICBzZWxlY3QoaGVpZ2h0LCB3aWR0aCkgJT4lIA0KICBtdXRhdGUoeCA9IGN1dCh3aWR0aCwgYnJlYWtzID0gbXlfY3V0cG9pbnRzLCBsYWJlbHMgPSBsYWJlbHMpKSAlPiUgDQogIG11dGF0ZSh5ID0gY3V0KGhlaWdodCwgYnJlYWtzID0gbXlfY3V0cG9pbnRzLCBsYWJlbHMgPSBsYWJlbHMpKQ0KDQptYXBfZGF0YSA8LSBtYXBfZGF0YSAlPiUgdW5pdGUoaWQsIGMoInkiLCAieCIpLCBzZXAgPSAiIikNCg0KDQpnZ3Bsb3QobWFwX2RhdGEpICsNCiAgZ2VvbV9zZihhZXMoZmlsbCA9IGlkKSkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjb2xvcl9wYWxldHRlJGNvbG9yKSArDQogIG15X21hcF90aGVtZSgpDQoNCg0KDQpgYGANCg0KDQojIyBXYXNoaW5ndG9uIFBvc3QgTWFwDQoNCmBgYHtyfQ0KDQpHcmFwaF9CaXZhcmlhdGVfQ292aWRfTWFwKCIyMDIxLTA5LTIzIiwgZ2VuZXJhdGVfYml2YXJpYXRlX3BhbGV0dGUoZ2V0X3dwX3BhbGV0dGUoKSkpDQpHcmFwaF9CaXZhcmlhdGVfQ292aWRfTWFwKCIyMDIxLTA5LTIzIiwgZ2VuZXJhdGVfYml2YXJpYXRlX3BhbGV0dGUoZ2V0X3JiX3BhbGV0dGUoKSkpDQoNCmBgYA0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KLi4uDQoNCg==